﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace ModbusTcpConsoleAppDemo
{
    internal class Program
    {
        static Socket _socket;

        public static void Connect()
        {
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            _socket.Connect(IPAddress.Parse("127.0.0.1"), 502);

            if (_socket.Connected)
            {
                Console.WriteLine("已连接服务器端~~~");
            }
            else
            {
                Console.WriteLine("连接服务器端失败~~~");
            }

        }

        static void Main(string[] args)
        {
            Connect();

            SendMessage();

            Console.ReadKey();
        }

        public static void SendMessage()
        {
            /* modbus tcp报文 --- 报文高位在前  --- C#为小端字节序
           *  MBAP:   
           *          1.事务元标识符： 占两个字节  客户端发送 这里设置为 0x00 0x01
           *          2.协议标识符：   占两个字节  modbus 规定为  0x00 0x00
           *          3.长度：        从单元标识符开始往后计数 占两个字节   客户端发送 规定为 0x00 0x06
           *          4.单元标识符：   替代RTU的从站地址， 占一个字节     默认为0x01
           * 
           *  其余报文： 
           *          1.功能码：    一个字节
           *              01：读线圈 读线圈状态 位操作
                          02：读离散量输入状态  位操作
                          03：读保持寄存器（每个寄存器含有两个字节）  字操作
                          04：读输入寄存器      字操作
                          05：写单个线圈
                          06：写单个寄存器
                          15：用于写多个线圈
                          16：写多个寄存器
           *          2.操作数据起始地址：  两个字节
           *          3.操作数据数量：     两个字节
           */

            byte[] buffer = new byte[12];

            //事务元标识符
            buffer[0] = 0x00;
            buffer[1] = 0x01;
            //协议标识符
            buffer[2] = 0x00;
            buffer[3] = 0x00;
            //长度
            buffer[4] = 0x00;
            buffer[5] = 0x06;
            // 单元标识符
            buffer[6] = 0x01;
            //功能码
            buffer[7] = 0x03;    //读取保持寄存器
            //操作数据起始地址
            buffer[8] = 0x00;
            buffer[9] = 0x00;
            //操作数据数量
            buffer[10] = 0x00;
            buffer[11] = 0x03;  //读取3个保持寄存器


            _socket.Send(buffer);

            byte[] buffer2 = new byte[1024];

            _socket.Receive(buffer2);

            /*
             *  传回报文:  MBAP + 功能码 + 数据字节数(一个字节)
             *  数据长度=长度-单元标识符-数据字节数
             *  
             */

            // 数据的长度
            int receiveDataLength = (buffer2[4] * 256) + buffer2[5] - 3; // 获取返回的有效数据字节数

            byte[] buffer3 = new byte[receiveDataLength];
            //buffer2中，前面九个字节为MBAP+功能码+返回的字节数量(所以要从第十个字节开始拿取数据)
            Array.Copy(buffer2, 9, buffer3, 0, receiveDataLength);

            //foreach (byte item in buffer3)
            //{
            //    Console.Write(item + "\t");
            //}

            // 这里只读取保持寄存器一个值（2个字节），直接将两个字节转换为数据
            //Console.WriteLine();
            //Console.WriteLine(IPAddress.HostToNetworkOrder(BitConverter.ToInt16(buffer3,0)));

            // BitConverter.ToInt16(buffer3,0) //转换过来的默认是小端的,Modbus协议用大端发送，所以用IPAddress.HostToNetworkOrder()将其转换为大端。
            // IPAddress.HostToNetworkOrder()// 大端
            // IPAddress.NetworkToHostOrder(); // 小端

            for (int i = 0; i < receiveDataLength; i+=2)
            {
                Console.Write(IPAddress.HostToNetworkOrder(BitConverter.ToInt16(buffer3, i))+"\t");
            }


        }



    }
}
